﻿using System.Diagnostics;
using System.Text;
using Agile.Infra.Core.Agile.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.IO;

namespace Agile.Shared.WebApi.Middlewares;

public class HttpLoggingMiddleware
{
    private readonly RequestDelegate _next;
    private const int MaxContentLengthToLog = 1024 * 1024; // 1MB
    private static readonly HashSet<string> textContentTypePrefixes = new(StringComparer.OrdinalIgnoreCase)
    {
        "application/json",
        "application/xml",
        "text/",
        "application/javascript",
        "application/x-www-form-urlencoded"
    };

    public HttpLoggingMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context, IHttpLoggingService _httpLoggingService)
    {
        if (!ShouldLogRequest(context))
        {
            await _next(context);
            return;
        }

        var stopwatch = Stopwatch.StartNew();

        // 保存原始响应流
        var originalResponseBody = context.Response.Body;

        try
        {
            // 记录请求信息
            var log = new HttpLoggingModel();
            await GetRequestAsync(log, context);

            using (var responseBuffer = new RecyclableMemoryStreamManager().GetStream())
            {
                context.Response.Body = responseBuffer;

                // 处理请求
                await _next(context);

                stopwatch.Stop();

                // 记录响应信息
                await GetResponseAsync(log, context, responseBuffer);

                // 计算耗时
                log.UsedTimeMillisecond = stopwatch.ElapsedMilliseconds;

                //_logger.LogInformation($"请求日志：{log.ToJsonString()}");
                _ = Task.Run(async () =>
                {
                    try
                    {
                        await _httpLoggingService.SaveAsync(log);
                    }
                    catch (Exception ex)
                    {
                        context.RequestServices.GetService<ILogger<HttpLoggingMiddleware>>()?.LogError(ex, "http请求日志保存异常");
                    }
                });

                // 将响应内容复制回原始流
                responseBuffer.Seek(0, SeekOrigin.Begin);
                await responseBuffer.CopyToAsync(originalResponseBody);
            }
        }
        catch (Exception ex)
        {
            context.RequestServices.GetService<ILogger<HttpLoggingMiddleware>>()?.LogError(ex, "http请求日志异常");
        }
        finally
        {
            context.Response.Body = originalResponseBody;
        }
    }

    private bool ShouldLogRequest(HttpContext context)
    {
        // 排除的路径
        if (context.Request.Path == "/") return false;

        return true;
    }

    private async Task GetRequestAsync(HttpLoggingModel log, HttpContext context)
    {
        var request = context.Request;
        log.ClientIP = context.GetClientIp();
        log.ServerIP = string.Empty;
        log.RequestTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff");
        log.RequestHost = $"{request.Scheme}://{request.Host.ToString()}";
        log.RequestPath = request.Path;
        log.RequestQuery = request.QueryString.ToString();
        log.RequestMethod = request.Method;
        log.RequestAuthorization = GetHeaderValue(request.Headers, "Authorization");
        log.RequestContentType = GetHeaderValue(request.Headers, "Content-Type");
        log.RequestUserAgent = GetHeaderValue(request.Headers, "User-Agent");
        log.RequestContentLength = request.ContentLength ?? 0;

        // 只记录内容类型为文本的请求体
        if (request.ContentLength > 0 && request.ContentLength < MaxContentLengthToLog && IsTextContentType(request.ContentType))
        {
            // 启用多次读取请求体
            request.EnableBuffering();

            using (var reader = new StreamReader(
                request.Body,
                encoding: Encoding.UTF8,
                detectEncodingFromByteOrderMarks: false,
                bufferSize: 1024,
                leaveOpen: true))
            {
                log.RequestBody = await reader.ReadToEndAsync();
                request.Body.Position = 0; // 重置流位置以供后续中间件读取
            }
        }
        else if (request.ContentLength > 0)
        {
            log.RequestBody = $"[Binary data: {request.ContentLength} bytes]";

            if (IsFileUploadRequest(request))
            {
                log.RequestBody = $"[File upload: {request.ContentLength} bytes]";
            }
        }
    }

    private async Task GetResponseAsync(HttpLoggingModel log, HttpContext context, Stream responseStream)
    {
        var response = context.Response;
        responseStream.Seek(0, SeekOrigin.Begin);

        log.ResponseCode = response.StatusCode.ToString();
        log.ResponseTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff");
        log.ResponseContentLength = responseStream.Length;
        log.ResponseContentType = GetHeaderValue(context.Response.Headers, "Content-Type");

        // 只记录内容类型为文本的响应体
        if (responseStream.Length > 0 && responseStream.Length < MaxContentLengthToLog && IsTextContentType(response.ContentType))
        {
            using (var reader = new StreamReader(
                responseStream,
                encoding: Encoding.UTF8,
                detectEncodingFromByteOrderMarks: false,
                bufferSize: 1024,
                leaveOpen: true))
            {
                log.ResponseBody = await reader.ReadToEndAsync();
                responseStream.Seek(0, SeekOrigin.Begin); // 重置流位置
            }
        }
        else if (responseStream.Length > 0)
        {
            log.ResponseBody = $"[Binary data: {responseStream.Length} bytes]";

            if (IsFileDownloadResponse(response))
            {
                log.ResponseBody = $"[File download: {responseStream.Length} bytes]";
            }
        }
    }

    private bool IsTextContentType(string? contentType)
    {
        if (string.IsNullOrEmpty(contentType))
            return false;

        return textContentTypePrefixes.Any(t => contentType.StartsWith(t, StringComparison.OrdinalIgnoreCase));
    }

    private bool IsFileUploadRequest(HttpRequest request)
    {
        return request.ContentType != null &&
              (request.ContentType.StartsWith("image/") ||
               request.ContentType.StartsWith("video/") ||
               request.ContentType.StartsWith("audio/") ||
               request.ContentType == "application/octet-stream" ||
               request.ContentType == "multipart/form-data");
    }

    private bool IsFileDownloadResponse(HttpResponse response)
    {
        var contentType = response.ContentType;
        return contentType != null &&
              (contentType.StartsWith("image/") ||
               contentType.StartsWith("video/") ||
               contentType.StartsWith("audio/") ||
               contentType == "application/octet-stream" ||
               contentType == "application/pdf" ||
               contentType == "application/zip");
    }

    private string GetHeaderValue(IHeaderDictionary header, string key)
    {
        if (header == null) return null;

        if (header.Any(a => key.EqualsIgnoreCase(a.Key)) == false) return null;

        return header[key];
    }
}